# 剑指Offer题解 - Day26
# 剑指 Offer 52. 两个链表的第一个公共节点
输入两个链表,找出它们的第一个公共节点。
示例1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
1
2
3
2
3
注意:
- 如果两个链表没有交点,返回 null。
- 在返回结果后,两个链表仍须保持原有的结构。
- 可假定整个链表结构中没有循环。
- 程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。
思路:
此题求两个链表的第一个公共节点。题目要求时间复杂度是O(n)
,空间复杂度是O(1)
,这里使用双指针进行求解。
首先,将两个链表的长度分别记为a
和b
,公共部分的长度记为c
。那么从表头走到公共节点的距离就是(a - c)
和(b - c)
。
此时,记录两个指针A
和B
分别从链表的表头出发,走完当前链表后再走另一个链表,直到公共节点。可以得出下面的公式:
a + (b - c) = b + (a - c)
此时分为两种情况:
- 如果
c = 0
,意味着两个链表没有公共尾部,此时A
和B
均指向null
。 - 如果
c > 0
,意味着两个链表有公共尾部,此时A
和B
均指向第一个公共节点。
因此,不管有没有公共节点,最终只需返回A
或者B
即可。
# 双指针
/**
* @param {ListNode} headA
* @param {ListNode} headB
* @return {ListNode}
*/
var getIntersectionNode = function(headA, headB) {
let A = headA;
let B = headB;
while(A !== B) {
A = A !== null ? A.next : headB;
B = B !== null ? B.next : headA;
}
return A;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
- 时间复杂度 O(m + n)。
- 空间复杂度 O(1)。
分析:
首先,声明两个变量分别指向两个链表的头部节点。按照上面的分析,A === B
有两种情况,一种是都指向null
;一种是都指向第一个公共节点。
循环内部,指针先遍历完当前链表,然后遍历另一个链表,直到相遇或者为null
。遍历结束后,直接返回A
或者B
即可。因为此时A
或者B
指向的就是第一个公共节点或者null
。
复杂度方面,最坏情况下,需要遍历完两个链表,因此时间复杂度是O(m + n)
;节点指针 A
, B
使用常数大小的额外空间,因此空间复杂度是O(1)
。
# 总结
本题巧妙的使用遍历链表的方式获取第一个公共节点。遇到链表问题,首先需要想到双指针解法,需要牢记在心。